﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ServiceBus.Messaging;
using GrayParrot.Configuration;
using GrayParrot.ServiceBus.Configuration;
using GrayParrot.Core.ServiceBus;

namespace GrayParrot.ServiceBus
{
    public abstract class Subscriber<T> : IDisposable, ISubscriber<BrokeredMessage> 
        where T : class
    {
        public event EventHandler<Exception> OnConnectionFailed;
        public event EventHandler OnStarted;
        public event EventHandler OnStopped;
        //public event EventHandler<Exception> OnError;

        private string Endpoint { get; set; }
        private string Topic { get; set; }
        private string Subscription { get; set; }
        public bool ReadDL { get; set; }

        //private volatile bool m_shouldStop;
        // Backing fields for the ServiceStopRequested property.
        private static readonly object LockFlag = new object();
        private bool m_ServiceStopRequested = false;

        SubscriptionClient m_client = null;

        private bool ServiceStopRequested
        {
            get
            {
                lock (LockFlag)
                {
                    return m_ServiceStopRequested;
                }
            }
            set
            {
                lock (LockFlag)
                {
                    m_ServiceStopRequested = value;
                }
            }
        }

        public Subscriber()
        {
            //Init();
        }

        public Subscriber(string subscription)
        {
            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            Subscription m_subs = pp.GetSubscription(subscription);

            Endpoint = m_subs.ConnectionString;
            Topic = m_subs.Topic;
            Subscription = subscription;
            ReadDL = m_subs.ReadDL;

            //m_shouldStop = false;
            ServiceStopRequested = false;
        }

        public Subscriber(string endpoint, string topic, string subscription)
        {
            Endpoint = endpoint;
            Topic = topic;
            Subscription = subscription;

            //m_shouldStop = false;
            ServiceStopRequested = false;
            //Init();
        }

        public void RequestStop()
        {
            //m_shouldStop = true;
            ServiceStopRequested = true;
        }

        public void StartListen()
        {
            try
            {
                if (m_client != null)
                {
                    if (OnStarted != null)
                    {
                        OnStarted(this, EventArgs.Empty);
                    }

                    GrayParrot.Logging.Logger.For<T>().Info("... connection established");

                    m_client.Receive();
                    
                    // Continuously process messages received from the subscription 
                    while (!this.ServiceStopRequested)
                    {
                        BrokeredMessage message = m_client.Receive();

                        if (message != null)
                        {
                            try
                            {
                                Receive(message);

                                // Remove message from subscription
                                message.Complete();
                            }
                            catch (MessageLockLostException mllex)
                            {
                                GrayParrot.Logging.Logger.For<T>().Error(string.Format("{0} MessageLockLost Abandon message: {1}", message.MessageId, mllex.Message));
                            }
                            catch (Exception ex)
                            {
                                string msg = ex.Message;

                                GrayParrot.Logging.Logger.For<T>().Error(string.Format("{0} Exception Abandon message: {1}", message.MessageId, ex.Message));

                                // Indicate a problem, unlock message in subscription
                                message.Abandon();
                            }
                        }
                    }
                }
            }
            catch (TimeoutException timeoEx)
            {
                GrayParrot.Logging.Logger.For<T>().Error(timeoEx);

                throw;

                //EstablishConnection();
            }
            catch (MessagingCommunicationException mcommEx)
            {
                GrayParrot.Logging.Logger.For<T>().Error(mcommEx);

                throw;
                //EstablishConnection();
            }
            catch (ServerBusyException sbEx)
            {
                GrayParrot.Logging.Logger.For<T>().Error(sbEx);

                throw;
                //EstablishConnection();
            }
            catch (UnauthorizedAccessException uaEx)
            {
                GrayParrot.Logging.Logger.For<T>().Error(uaEx);

                throw;
                //EstablishConnection();
            }
            catch (CommunicationException cmmEx)
            {
                GrayParrot.Logging.Logger.For<T>().Error(cmmEx);

                throw;
                //EstablishConnection();
            }
            catch (Exception ex)
            {
                GrayParrot.Logging.Logger.For<T>().Error(ex);

                throw;
                //EstablishConnection();
            }
        }

        private void EstablishConnection()
        {
            // wait 5 seconds and try to reconnect...
            Thread.Sleep(5000);

            m_client = null;

            // 
            Init();
        }

        public abstract bool Receive(BrokeredMessage message);

        //public void Receive(Action<BrokeredMessage> callback, Action onErrorCallback)
        //{
        //    if (m_client != null)
        //    {
        //        m_client.Receive();

        //        // Continuously process messages received from the subscription 
        //        while (true)
        //        {
        //            BrokeredMessage message = m_client.Receive();

        //            if (message != null)
        //            {
        //                try
        //                {
        //                    callback(message);

        //                    // Remove message from subscription
        //                    message.Complete();
        //                }
        //                catch (Exception)
        //                {
        //                    // Indicate a problem, unlock message in subscription
        //                    message.Abandon();

        //                    onErrorCallback();
        //                }
        //            }
        //        }
        //    }
        //}

        //private void Init()
        //{
        //    m_client = SubscriptionClient.CreateFromConnectionString(Endpoint, Topic, Subscription);

        //    if (m_client != null)
        //    {
        //        StartListen();
        //    }
        //}

        public void Init()
        {
            try
            {
                if (!ReadDL)
                {
                    GrayParrot.Logging.Logger.For<T>().Info("Create connection...");
                    m_client = SubscriptionClient.CreateFromConnectionString(Endpoint, Topic, Subscription);
                }
                else
                {
                    // https://blogs.blackmarble.co.uk/blogs/sspencer/default.aspx?PageIndex=2&page=2
                    GrayParrot.Logging.Logger.For<T>().Info("Create connection to DL...");
                    m_client = SubscriptionClient.CreateFromConnectionString(Endpoint, Topic, Subscription + "/$DeadLetterQueue");                   
                }

                if (m_client != null)
                {
                    StartListen();
                }
                else
                {
                    GrayParrot.Logging.Logger.For<T>().Error("Connection to Service bus Failed !");

                    if (OnConnectionFailed != null)
                    {
                        OnConnectionFailed(this, new Exception("Connection to Service bus Failed !"));
                    }
                }
            }
            catch (Exception ex)
            {
                GrayParrot.Logging.Logger.For<T>().Error("Connection to Service bus Failed !", ex);

                if (OnConnectionFailed != null)
                {
                    OnConnectionFailed(this, ex);
                }
            }
        }

        public void Dispose()
        {
            this.RequestStop();

            if (m_client != null)
            {
                m_client.Close();
                m_client = null;

                if (OnStopped != null)
                {
                    OnStopped(this, EventArgs.Empty);
                }
            }

            GC.SuppressFinalize(this);
        }
    }
}
